package paim.wingchun.view.componentes.toolbar;


import java.awt.Dimension;
import java.util.EventObject;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.logging.Level;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.event.EventListenerList;

import paim.wingchun.app.WC_Log;
import paim.wingchun.controller.WC_Event;
import paim.wingchun.view.FireListener;
import paim.wingchun.view.Fireable;

/** @author paim 18/01/2011 */
public class JNavegador extends AbstractJToolBar implements Fireable<NavegadorListener> {

    private static final long serialVersionUID = 1L;

    protected enum Eventos {
        PROXIMO, ANTERIOR, ULTIMO, PRIMEIRO, CARREGAPOSICAO
    }

    private JButton jProximo;

    private JButton jAnterior;

    private JButton jPrimeiro;

    private JButton jUltimo;

    private JLabel jRotulo;

    private JLabel jPosicao;

    private String rotulo;

    private Vector<Long> registros = new Vector<Long>();

    private int posicao = -1;

    private boolean enabled;

    protected enum Localizacao {
        SEILA, UNICO, PRIMEIRO, ULTIMO, INTERMEDIARIO
    }

    private Localizacao localizacao;

    private final boolean showTotal;

    public JNavegador() {
        this("");
    }

    public JNavegador(String titulo) {
        this(titulo, true);
    }

    public JNavegador(String rotulo, boolean showTotal) {
        super();
        this.rotulo = rotulo;
        this.showTotal = showTotal;
        initialize();
    }

    @Override
    public EventListenerList getListenersList() {
        return this.listenerList;
    }


    /** troca o ultimo null por um id persistido, em principio, null so pode ser o ultimo
     *
     * @author paim 26/05/2011
     * @param id
     *            void */
    public void refreshLastRegistro(Long id) {
        int index = registros.lastIndexOf(null);
        if ( index >= 0 )
            registros.setElementAt(id, index);
    }

    /** adiciona um registro ao vetor */
    public void add() {
        /* adiciona um elemento */
        registros.add(null);
        /* e posiciona no ultimo */
        setPosicao(registros.size() - 1);
    }

    /** remove o elemento e retorna o id do elemento anterior */
    public Long remove() {

        if ( posicao < 0 ) {
            try {
                throw new NoSuchElementException("Tentativa de remocao de um índice negativo, debug field posicao");
            }
            catch ( NoSuchElementException e ) {
                WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
            }
        }

        /* remove */
        registros.removeElementAt(posicao);

        /* posiciona */
        setPosicao(posicao - 1);

        fire(new WC_Event(Eventos.CARREGAPOSICAO, new EventObject(this)));

        /* Retorno de -1 significa que navegador passou a estar vazio */
        if ( registros.isEmpty() )
            return -1L;
        /* Retorna id */
        return registros.elementAt(posicao);

    }

    /** coloca esses elementos no navegador */
    public void addAll(List<Long> ids) {
        if ( ids == null )
            return;
        clear();
        registros.addAll(ids);
    }

    /** remove todos item da lista */
    public void clear() {
        registros.removeAllElements();

        posicao = -1;

        ajustajLabelPosicao();
        setEnabled(false);
    }

    /** navega para esse elemento, observe que eh disparado o evento de carrega posicao
     *
     * @author paim 26/05/2011
     * @param id
     *            void */
    public void navega(Long id) {
        /* se nulo, posiciona no ultimo, senao posiciona no que ja existe */
        setPosicao(id == null ? registros.size() - 1 : registros.indexOf(id));

        /* carrega a devida posicao */
        fire(new WC_Event(Eventos.CARREGAPOSICAO, new EventObject(this)));
        /* sempre refresh o component */
        repaint();
    }

    public Object getRegistro() {
        if ( !registros.isEmpty() && posicao >= 0 )
            return registros.elementAt(posicao);
        else
            return null;
    }

    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        repaint();
    }

    @Override
    public void repaint() {
        super.repaint();

        /* necessario pois esta classe eh instanciada por reflexao e pode estar null inicialmente */
        if ( localizacao == null )
            return;

        switch ( localizacao ) {
            case SEILA:
            case UNICO:
                getjAnterior().setEnabled(false);
                getjPrimeiro().setEnabled(false);
                getjProximo().setEnabled(false);
                getjUltimo().setEnabled(false);
            break;
            case PRIMEIRO:
                getjAnterior().setEnabled(false);
                getjPrimeiro().setEnabled(false);
                getjProximo().setEnabled(enabled);
                getjUltimo().setEnabled(enabled);
            break;
            case INTERMEDIARIO:
                getjAnterior().setEnabled(enabled);
                getjPrimeiro().setEnabled(enabled);
                getjProximo().setEnabled(enabled);
                getjUltimo().setEnabled(enabled);
            break;
            case ULTIMO:
                getjAnterior().setEnabled(enabled);
                getjPrimeiro().setEnabled(enabled);
                getjProximo().setEnabled(false);
                getjUltimo().setEnabled(false);
            break;
        }
    }

    public Vector<Long> getRegistros() {
        return registros;
    }

    public boolean isEmpty() {
        return getRegistros().isEmpty();
    }

    JButton getjProximo() {
        if ( jProximo == null ) {
            jProximo = new JButtonP(Eventos.PROXIMO);
            jProximo.putClientProperty("nome", JNavegador.class.getSimpleName() + "."
                            + Eventos.PROXIMO.name().toLowerCase());
        }
        return jProximo;
    }

    JButton getjAnterior() {
        if ( jAnterior == null ) {
            jAnterior = new JButtonP(Eventos.ANTERIOR);
            jAnterior.putClientProperty("nome", JNavegador.class.getSimpleName() + ".anterior");
        }
        return jAnterior;
    }

    JButton getjPrimeiro() {
        if ( jPrimeiro == null ) {
            jPrimeiro = new JButtonP(Eventos.PRIMEIRO);
            jPrimeiro.putClientProperty("nome", JNavegador.class.getSimpleName() + ".primeiro");
        }
        return jPrimeiro;
    }

    JButton getjUltimo() {
        if ( jUltimo == null ) {
            jUltimo = new JButtonP(Eventos.ULTIMO);
            jUltimo.putClientProperty("nome", JNavegador.class.getSimpleName() + ".ultimo");
        }
        return jUltimo;
    }

    JLabel getjRotulo() {
        if ( jRotulo == null ) {
            jRotulo = new JLabel(rotulo);
        }
        return jRotulo;
    }

    JLabel getjPosicao() {
        if ( jPosicao == null ) {
            jPosicao = new JLabel("0 de 0");
            jPosicao.putClientProperty("nome", JNavegador.class.getSimpleName() + ".lposicao");

            Dimension d = new Dimension(50, 20);
            jPosicao.setMaximumSize(d);
            jPosicao.setMinimumSize(d);
            jPosicao.setPreferredSize(d);
        }
        return jPosicao;
    }

    @Override
    public void fire(WC_Event e) {
        //NavegadorListener[] listeners = getListenersList().getListeners(NavegadorListener.class);
        for ( NavegadorListener listener : fireListeners() ) {

            if ( (Eventos) e.evento() == Eventos.CARREGAPOSICAO ) {
                listener.carrega(getRegistro() != null ? new EventObject(getRegistro()) : null);
            }
            else {
                if ( !listener.podeNavegar() )
                    return;

                Object objeto = null;
                switch ( (Eventos) e.evento() ) {
                    case PROXIMO:
                        objeto = getPosterior();
                        listener.proximo(objeto == null ? null : new EventObject(objeto));
                    break;
                    case ANTERIOR:
                        objeto = getAnterior();
                        listener.anterior(objeto == null ? null : new EventObject(objeto));
                    break;
                    case PRIMEIRO:
                        objeto = getPrimeiro();
                        listener.primeiro(objeto == null ? null : new EventObject(objeto));
                    break;
                    case ULTIMO:
                        objeto = getUltimo();
                        listener.ultimo(objeto == null ? null : new EventObject(objeto));
                    break;

                    default:
                        continue;
                }
                setPosicao(objeto == null ? registros.size() - 1 : registros.indexOf((Long) objeto));
            }
            repaint();
        }

    }

    protected int getPosicao() {
        return posicao;
    }

    protected void setPosicao(int i) {
        /* Se a posicao atual for -1, mas existem registros, setar posicao para 0 */
        if ( i < 0 && !registros.isEmpty() )
            posicao = 0;
        else
            posicao = i;

        ajustajLabelPosicao();

        if ( posicao < 0 )
            localizacao = Localizacao.SEILA;
        else if ( registros.size() == 1 )
            localizacao = Localizacao.UNICO;
        else if ( posicao == 0 )
            localizacao = Localizacao.PRIMEIRO;
        /* Se estiver na ultima posicao */
        else if ( posicao >= registros.size() - 1 )
            localizacao = Localizacao.ULTIMO;
        else
            localizacao = Localizacao.INTERMEDIARIO;

    }

    protected void ajustajLabelPosicao() {
        StringBuilder s = new StringBuilder();
        s.append((posicao < 0 ? "?" : String.valueOf(posicao + 1)));
        if ( showTotal ) {
            s.append(" de ");
            s.append(String.valueOf(registros.size()));
        }
        getjPosicao().setText(s.toString());
        getjPosicao().repaint();
    }

    private void initialize() {
        /* remove qualquer coisa */
        this.removeAll();

        add(getjPrimeiro());
        add(getjAnterior());
        add(new JLabel(" "));
        add(getjRotulo());
        add(getjPosicao());
        add(getjProximo());

        add(getjUltimo());

        setBorderPainted(false);
        setFloatable(false);
        setRollover(true);

        getjAnterior().addActionListener(new FireListener(this, Eventos.ANTERIOR));
        getjPrimeiro().addActionListener(new FireListener(this, Eventos.PRIMEIRO));
        getjProximo().addActionListener(new FireListener(this, Eventos.PROXIMO));
        getjUltimo().addActionListener(new FireListener(this, Eventos.ULTIMO));
    }

    private Object getPrimeiro() {
        try {
            return registros.firstElement();
        }
        catch ( NoSuchElementException e ) {
            return null;
        }
    }

    private Object getUltimo() {
        try {
            return registros.lastElement();
        }
        catch ( NoSuchElementException e ) {
            return null;
        }
    }

    /** se estiver no ultimo, este metodo retornara null
     *
     * @author paim 26/05/2011
     * @return Object */
    private Object getPosterior() {
        try {
            return registros.elementAt(posicao + 1);
        }
        catch ( ArrayIndexOutOfBoundsException e ) {
            return null;
        }
    }

    private Object getAnterior() {
        try {
            return registros.elementAt(posicao - 1);
        }
        catch ( ArrayIndexOutOfBoundsException e ) {
            return null;
        }
    }
}
